const { expect } = require("chai");
const { ethers, upgrades } = require("hardhat");
const { time } = require("@nomicfoundation/hardhat-network-helpers");

describe("NFT拍卖市场测试", function () {
    let priceOracle, auctionNFT, auctionFactory, crossChainAuction;
    let owner, seller, bidder1, bidder2, feeReceiver;
    let mockETHUSDFeed;

    // 模拟Chainlink价格feed
    const MockAggregatorV3 = {
        deploy: async function () {
            const MockAggregator = await ethers.getContractFactory("MockV3Aggregator");
            return await MockAggregator.deploy(8, ethers.utils.parseUnits("2000", 8)); // $2000 ETH
        }
    };

    beforeEach(async function () {
        // 获取测试账户
        [owner, seller, bidder1, bidder2, feeReceiver] = await ethers.getSigners();

        // 部署模拟价格feed
        const MockAggregator = await ethers.getContractFactory("MockV3Aggregator");
        mockETHUSDFeed = await MockAggregator.deploy(8, ethers.utils.parseUnits("2000", 8));
        await mockETHUSDFeed.deployed();

        // 部署价格预言机
        const PriceOracle = await ethers.getContractFactory("PriceOracle");
        priceOracle = await upgrades.deployProxy(
            PriceOracle,
            [mockETHUSDFeed.address],
            { initializer: 'initialize' }
        );
        await priceOracle.deployed();

        // 部署NFT合约
        const AuctionNFT = await ethers.getContractFactory("AuctionNFT");
        auctionNFT = await upgrades.deployProxy(
            AuctionNFT,
            [
                "测试拍卖NFT",
                "TANFT",
                feeReceiver.address,
                250, // 2.5% royalty
                ethers.utils.parseEther("0.01"),
                1000
            ],
            { initializer: 'initialize' }
        );
        await auctionNFT.deployed();

        // 部署拍卖工厂
        const AuctionFactory = await ethers.getContractFactory("AuctionFactory");
        auctionFactory = await upgrades.deployProxy(
            AuctionFactory,
            [priceOracle.address, feeReceiver.address],
            { initializer: 'initialize' }
        );
        await auctionFactory.deployed();

        // 配置NFT合约
        await auctionNFT.setAuctionMarket(auctionFactory.address);
    });

    describe("💰 价格预言机测试", function () {
        it("应该正确获取ETH价格", async function () {
            const ethPrice = await priceOracle.getETHPrice();
            expect(ethPrice).to.equal(ethers.utils.parseUnits("2000", 8));
        });

        it("应该正确转换ETH到USD", async function () {
            const ethAmount = ethers.utils.parseEther("1"); // 1 ETH
            const usdValue = await priceOracle.convertETHToUSD(ethAmount);
            expect(usdValue).to.equal(ethers.utils.parseUnits("2000", 8)); // $2000
        });
    });

    describe("🎨 NFT合约测试", function () {
        it("应该正确铸造NFT", async function () {
            const mintTx = await auctionNFT.connect(seller).mintNFT(
                seller.address,
                "ipfs://test-metadata",
                { value: ethers.utils.parseEther("0.01") }
            );

            await expect(mintTx)
                .to.emit(auctionNFT, "NFTMinted")
                .withArgs(seller.address, 1, "ipfs://test-metadata", seller.address);

            expect(await auctionNFT.ownerOf(1)).to.equal(seller.address);
            expect(await auctionNFT.totalSupply()).to.equal(1);
        });

        it("应该支持批量铸造", async function () {
            const tokenURIs = ["ipfs://test1", "ipfs://test2", "ipfs://test3"];
            const mintTx = await auctionNFT.connect(seller).batchMintNFT(
                seller.address,
                tokenURIs,
                { value: ethers.utils.parseEther("0.03") }
            );

            expect(await auctionNFT.totalSupply()).to.equal(3);
            expect(await auctionNFT.balanceOf(seller.address)).to.equal(3);
        });
    });

    describe("🏭 拍卖工厂测试", function () {
        beforeEach(async function () {
            // 铸造测试NFT
            await auctionNFT.connect(seller).mintNFT(
                seller.address,
                "ipfs://test-nft",
                { value: ethers.utils.parseEther("0.01") }
            );

            // 授权工厂合约
            await auctionNFT.connect(seller).setApprovalForAll(auctionFactory.address, true);
        });

        it("应该成功创建拍卖", async function () {
            const startPrice = ethers.utils.parseEther("0.1");
            const reservePrice = ethers.utils.parseEther("0.2");
            const duration = 86400; // 1天

            const createTx = await auctionFactory.connect(seller).createAuction(
                auctionNFT.address,
                1, // token ID
                startPrice,
                reservePrice,
                duration
            );

            await expect(createTx)
                .to.emit(auctionFactory, "AuctionCreated");

            const auctionAddress = await auctionFactory.nftToAuction(auctionNFT.address, 1);
            expect(auctionAddress).to.not.equal(ethers.constants.AddressZero);
        });

        it("应该防止重复创建拍卖", async function () {
            const startPrice = ethers.utils.parseEther("0.1");
            const reservePrice = ethers.utils.parseEther("0.2");
            const duration = 86400;

            // 第一次创建
            await auctionFactory.connect(seller).createAuction(
                auctionNFT.address,
                1,
                startPrice,
                reservePrice,
                duration
            );

            // 第二次创建应该失败
            await expect(
                auctionFactory.connect(seller).createAuction(
                    auctionNFT.address,
                    1,
                    startPrice,
                    reservePrice,
                    duration
                )
            ).to.be.revertedWith("AuctionFactory: Auction already exists");
        });
    });

    describe("🏺 拍卖合约测试", function () {
        let auctionContract;

        beforeEach(async function () {
            // 铸造和授权NFT
            await auctionNFT.connect(seller).mintNFT(
                seller.address,
                "ipfs://test-nft",
                { value: ethers.utils.parseEther("0.01") }
            );
            await auctionNFT.connect(seller).setApprovalForAll(auctionFactory.address, true);

            // 创建拍卖
            const createTx = await auctionFactory.connect(seller).createAuction(
                auctionNFT.address,
                1,
                ethers.utils.parseEther("0.1"), // 起拍价
                ethers.utils.parseEther("0.2"), // 保留价
                86400 // 1天
            );

            const receipt = await createTx.wait();
            const event = receipt.events.find(e => e.event === "AuctionCreated");
            auctionContract = await ethers.getContractAt("NFTAuction", event.args.auctionContract);
        });

        it("应该接受有效的ETH出价", async function () {
            const bidAmount = ethers.utils.parseEther("0.15");

            const bidTx = await auctionContract.connect(bidder1).bidWithETH({
                value: bidAmount
            });

            await expect(bidTx)
                .to.emit(auctionContract, "BidPlaced");

            const highestBid = await auctionContract.highestBid();
            expect(highestBid.bidder).to.equal(bidder1.address);
            expect(highestBid.amount).to.equal(bidAmount);
        });

        it("应该拒绝低于起拍价的出价", async function () {
            const lowBid = ethers.utils.parseEther("0.05"); // 低于起拍价0.1

            await expect(
                auctionContract.connect(bidder1).bidWithETH({ value: lowBid })
            ).to.be.revertedWith("NFTAuction: Bid below start price");
        });

        it("应该正确处理多个出价", async function () {
            // 第一个出价
            await auctionContract.connect(bidder1).bidWithETH({
                value: ethers.utils.parseEther("0.15")
            });

            // 第二个更高的出价
            await auctionContract.connect(bidder2).bidWithETH({
                value: ethers.utils.parseEther("0.25")
            });

            const highestBid = await auctionContract.highestBid();
            expect(highestBid.bidder).to.equal(bidder2.address);
            expect(highestBid.amount).to.equal(ethers.utils.parseEther("0.25"));
        });

        it("应该在拍卖结束后正确结算", async function () {
            // 出价
            await auctionContract.connect(bidder1).bidWithETH({
                value: ethers.utils.parseEther("0.25")
            });

            // 快进到拍卖结束
            await time.increase(86401); // 1天+1秒

            // 结算拍卖
            const settleTx = await auctionContract.connect(seller).settleAuction();

            await expect(settleTx)
                .to.emit(auctionContract, "AuctionSettled");

            // 验证NFT转移
            expect(await auctionNFT.ownerOf(1)).to.equal(bidder1.address);
        });
    });

    describe("📊 统计和查询测试", function () {
        it("应该正确返回拍卖统计", async function () {
            const stats = await auctionFactory.getAuctionStats();
            expect(stats.total).to.equal(0);
            expect(stats.active).to.equal(0);
        });

        it("应该正确返回活跃拍卖列表", async function () {
            const activeAuctions = await auctionFactory.getActiveAuctions();
            expect(activeAuctions.length).to.equal(0);
        });
    });

    describe("🔧 管理功能测试", function () {
        it("应该允许所有者更新配置", async function () {
            await auctionFactory.updateAuctionConfig(
                3600,  // 1小时最小时长
                604800, // 7天最大时长
                ethers.utils.parseEther("0.01"), // 最小起拍价
                200,   // 2%手续费
                300,   // 3%最小增幅
                true   // 启用动态手续费
            );

            const config = await auctionFactory.auctionConfig();
            expect(config.minDuration).to.equal(3600);
            expect(config.platformFeeRate).to.equal(200);
        });

        it("应该拒绝非所有者的配置更新", async function () {
            await expect(
                auctionFactory.connect(seller).updateAuctionConfig(
                    3600, 604800, ethers.utils.parseEther("0.01"), 200, 300, true
                )
            ).to.be.revertedWith("Ownable: caller is not the owner");
        });
    });

    describe("🛡️ 安全测试", function () {
        it("应该防止重入攻击", async function () {
            // 这里可以添加重入攻击测试
            // 由于使用了ReentrancyGuard，应该能防止重入攻击
        });

        it("应该正确处理紧急暂停", async function () {
            await auctionFactory.emergencyPauseAll();

            // 暂停后应该无法创建新拍卖
            await auctionNFT.connect(seller).mintNFT(
                seller.address,
                "ipfs://test",
                { value: ethers.utils.parseEther("0.01") }
            );
            await auctionNFT.connect(seller).setApprovalForAll(auctionFactory.address, true);

            await expect(
                auctionFactory.connect(seller).createAuction(
                    auctionNFT.address,
                    1,
                    ethers.utils.parseEther("0.1"),
                    ethers.utils.parseEther("0.2"),
                    86400
                )
            ).to.be.revertedWith("Pausable: paused");
        });
    });

    describe("🌉 跨链功能测试", function () {
        it("应该正确初始化跨链拍卖", async function () {
            const supportedChains = [1, 137, 56]; // Ethereum, Polygon, BSC

            await crossChainAuction.initializeCrossChainAuction(
                seller.address,
                auctionNFT.address,
                1,
                1, // NFT在Ethereum
                ethers.utils.parseEther("0.1"),
                86400,
                supportedChains
            );

            const auctionInfo = await crossChainAuction.auctionInfo();
            expect(auctionInfo.seller).to.equal(seller.address);
            expect(auctionInfo.isActive).to.be.true;
        });

        it("应该正确管理支持的链", async function () {
            await crossChainAuction.addSupportedChain(1, 5009297550715157269n); // Ethereum

            const isSupported = await crossChainAuction.supportedChains(1);
            expect(isSupported).to.be.true;

            const supportedChains = await crossChainAuction.getSupportedChains();
            expect(supportedChains).to.include(1);
        });
    });

    describe("💎 集成测试", function () {
        it("应该支持完整的拍卖流程", async function () {
            // 1. 铸造NFT
            await auctionNFT.connect(seller).mintNFT(
                seller.address,
                "ipfs://integration-test",
                { value: ethers.utils.parseEther("0.01") }
            );

            // 2. 授权工厂合约
            await auctionNFT.connect(seller).setApprovalForAll(auctionFactory.address, true);

            // 3. 创建拍卖
            const createTx = await auctionFactory.connect(seller).createAuction(
                auctionNFT.address,
                1,
                ethers.utils.parseEther("0.1"),
                ethers.utils.parseEther("0.2"),
                86400
            );

            const receipt = await createTx.wait();
            const event = receipt.events.find(e => e.event === "AuctionCreated");
            const auctionAddress = event.args.auctionContract;
            const auction = await ethers.getContractAt("NFTAuction", auctionAddress);

            // 4. 出价
            await auction.connect(bidder1).bidWithETH({
                value: ethers.utils.parseEther("0.25")
            });

            // 5. 验证出价
            const highestBid = await auction.highestBid();
            expect(highestBid.bidder).to.equal(bidder1.address);

            // 6. 快进到拍卖结束
            await time.increase(86401);

            // 7. 结算拍卖
            await auction.connect(seller).settleAuction();

            // 8. 验证NFT转移
            expect(await auctionNFT.ownerOf(1)).to.equal(bidder1.address);
        });

        it("应该正确计算和分配手续费", async function () {
            // 铸造NFT并创建拍卖
            await auctionNFT.connect(seller).mintNFT(
                seller.address,
                "ipfs://fee-test",
                { value: ethers.utils.parseEther("0.01") }
            );
            await auctionNFT.connect(seller).setApprovalForAll(auctionFactory.address, true);

            const createTx = await auctionFactory.connect(seller).createAuction(
                auctionNFT.address,
                1,
                ethers.utils.parseEther("0.1"),
                ethers.utils.parseEther("0.2"),
                86400
            );

            const receipt = await createTx.wait();
            const auctionAddress = receipt.events.find(e => e.event === "AuctionCreated").args.auctionContract;
            const auction = await ethers.getContractAt("NFTAuction", auctionAddress);

            // 记录初始余额
            const initialFeeReceiverBalance = await feeReceiver.getBalance();
            const initialSellerBalance = await seller.getBalance();

            // 出价
            const bidAmount = ethers.utils.parseEther("1.0");
            await auction.connect(bidder1).bidWithETH({ value: bidAmount });

            // 结束拍卖
            await time.increase(86401);
            await auction.connect(seller).settleAuction();

            // 验证手续费分配
            const finalFeeReceiverBalance = await feeReceiver.getBalance();
            const feeReceived = finalFeeReceiverBalance.sub(initialFeeReceiverBalance);

            // 预期手续费：1 ETH * 2.5% = 0.025 ETH
            const expectedFee = bidAmount.mul(250).div(10000);
            expect(feeReceived).to.be.closeTo(expectedFee, ethers.utils.parseEther("0.001"));
        });
    });

    describe("⚡ 性能和Gas测试", function () {
        it("应该在合理的Gas限制内完成操作", async function () {
            // 铸造NFT
            const mintTx = await auctionNFT.connect(seller).mintNFT(
                seller.address,
                "ipfs://gas-test",
                { value: ethers.utils.parseEther("0.01") }
            );
            const mintReceipt = await mintTx.wait();
            console.log("铸造NFT Gas消耗:", mintReceipt.gasUsed.toString());

            // 创建拍卖
            await auctionNFT.connect(seller).setApprovalForAll(auctionFactory.address, true);
            const createTx = await auctionFactory.connect(seller).createAuction(
                auctionNFT.address,
                1,
                ethers.utils.parseEther("0.1"),
                ethers.utils.parseEther("0.2"),
                86400
            );
            const createReceipt = await createTx.wait();
            console.log("创建拍卖 Gas消耗:", createReceipt.gasUsed.toString());

            // 验证Gas消耗在合理范围内
            expect(mintReceipt.gasUsed).to.be.below(200000);
            expect(createReceipt.gasUsed).to.be.below(500000);
        });
    });
});
